using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Linq;

namespace DeviceConfigTool
{
    public class UdpScanner
    {
        private const int BroadcastPort = 1901;
        private byte[] ScanHeader = new byte[] { 0x20, 0x25, 0x04, 0x55 }; // Scan packet header
        private byte[] ConfigHeader = new byte[] { 0x20, 0x25, 0x04, 0xAA }; // Config packet header
        private const byte SuccessCode = 0x00;
        private const byte FailureCode = 0xFF;
        private bool _isScanning;
        private Thread _receiveThread;
        private UdpClient _udpClient;

        public event Action<DeviceInfo> DeviceDiscovered;
        public event Action<bool, string> ConfigStatusReceived = (success, message) => {}; // Initialize with empty handler

        public UdpScanner() {
            // var localIp = IPAddress.Parse("192.168.0.100");
            // var localEndpoint = new IPEndPoint(localIp, 0); // port=0 means allocated by system.
            // _udpClient = new UdpClient(localEndpoint);
           _udpClient = new UdpClient();
        }

        public void reBindUdpClient(IPEndPoint localEndpoint){
          if(_udpClient != null){
            _udpClient.Close();
          }
          _udpClient = new UdpClient(localEndpoint);
        }

        public void StartScanning()
        {
            if (_isScanning) return;

            _isScanning = true;
            //@Debug

            //_udpClient = new UdpClient();
            _udpClient.EnableBroadcast = true;
            _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

            // Start listening for responses
            _receiveThread = new Thread(ReceiveResponses);
            _receiveThread.IsBackground = true;
            _receiveThread.Start();

            // Send scan packet
            _udpClient.Send(ScanHeader, ScanHeader.Length, new IPEndPoint(IPAddress.Broadcast, BroadcastPort));
        }

        public void StopScanning()
        {
            _isScanning = false;
            // _udpClient?.Close();
            _receiveThread?.Join(1000);
        }

        private void ReceiveResponses()
        {
            var remoteEndPoint = new IPEndPoint(IPAddress.Any, BroadcastPort);

            byte[] receivedBytes = null;
            DeviceInfo deviceInfo = null;
            try
            {
                while (_isScanning)
                {
                    receivedBytes = _udpClient.Receive(ref remoteEndPoint);
                    if (receivedBytes.Length > 4) // Minimum response size
                    {
                        deviceInfo = ParseDeviceResponse(receivedBytes, remoteEndPoint.Address);
                        DeviceDiscovered?.Invoke(deviceInfo);
                    }
                }
            }
            catch (Exception)
            {
                // Expected when stopping
            }
        }

        public void SendConfiguration(byte[] configData, DeviceInfo device = null)
        {
            if (!_isScanning)
            {
                _udpClient.EnableBroadcast = true;
            }

            // Parse MAC address into bytes
            byte[] macBytes = new byte[6];
            if (device != null && !string.IsNullOrEmpty(device.MacAddress))
            {
                var macParts = device.MacAddress.Split(':');
                for (int i = 0; i < 6; i++)
                {
                    macBytes[i] = Convert.ToByte(macParts[i], 16);
                }
            }

            // Create packet with:
            // - Config header (4 bytes)
            // - MAC address (6 bytes)
            // - Config data
            var packet = new byte[ConfigHeader.Length + 6 + configData.Length];
            Buffer.BlockCopy(ConfigHeader, 0, packet, 0, ConfigHeader.Length);
            Buffer.BlockCopy(macBytes, 0, packet, ConfigHeader.Length, 6);
            Buffer.BlockCopy(configData, 0, packet, ConfigHeader.Length + 6, configData.Length);

            _udpClient.Send(packet, packet.Length, new IPEndPoint(IPAddress.Broadcast, BroadcastPort));
        }

        private DeviceInfo ParseDeviceResponse(byte[] data, IPAddress ipAddress)
        {
            var deviceInfo = new DeviceInfo { IpAddress = ipAddress.ToString() };
            int i = 0;
            if (null == data) {
              return new DeviceInfo
              {
                  IpAddress = ipAddress.ToString(),
                  MacAddress = "00:00:00:00:00:00", // Parse from data
                  SubnetMask = "255.255.255.0", // Parse from data
                  Gateway = "192.168.1.1", // Parse from data
                  SerialNumber = "SN12345", // Parse from data
                  SoftwareVersion = "1.0.0" // Parse from data
              };
            }
            
            while (i < data.Length)
            {
                byte code = data[i++];
                byte len = data[i++];
                
                switch (code)
                {
                    case 0x01: // MAC address (6 bytes)
                        if (len == 6)
                        {
                            deviceInfo.MacAddress = string.Join(":", data.Skip(i).Take(6).Select(b => b.ToString("X2")));
                            i += 6;
                        }
                        break;
                        
                    case 0x02: // DHCP enable (1 byte)
                        if (len == 1)
                            deviceInfo.UseDhcp = data[i++] == 0;
                        break;
                        
                    case 0x03: // IPv4 address (4 bytes)
                        if (len == 4)
                        {
                            deviceInfo.IpAddress = string.Join(".", data.Skip(i).Take(4));
                            i += 4;
                        }
                        break;
                        
                    case 0x04: // Subnet mask (4 bytes)
                        if (len == 4)
                        {
                            deviceInfo.SubnetMask = string.Join(".", data.Skip(i).Take(4));
                            i += 4;
                        }
                        break;
                        
                    case 0x05: // Gateway (4 bytes)
                        if (len == 4)
                        {
                            deviceInfo.Gateway = string.Join(".", data.Skip(i).Take(4));
                            i += 4;
                        }
                        break;
                        
                    case 0x08: // Cloud server IP (4 bytes)
                        if (len == 4)
                        {
                            deviceInfo.CloudServerIp = string.Join(".", data.Skip(i).Take(4));
                            i += 4;
                        }
                        break;
                        
                    case 0x09: // Serial number (variable length)
                        if (len > 0 && i + len <= data.Length)
                        {
                            deviceInfo.SerialNumber = System.Text.Encoding.ASCII.GetString(data, i, len);
                            i += len;
                        }
                        break;
                        
                    case 0x0A: // Software version (variable length)
                        if (len > 0 && i + len <= data.Length)
                        {
                            deviceInfo.SoftwareVersion = System.Text.Encoding.ASCII.GetString(data, i, len);
                            i += len;
                        }
                        break;
                        
                    default: // Unknown code - skip
                        i += len;
                        break;
                }
            }
            
            return deviceInfo;
        }

    }
}
